package com.zmx.nfcdemo;

import java.io.IOException;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentFilter.MalformedMimeTypeException;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.TagLostException;
import android.nfc.tech.MifareClassic;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity {

    private IntentFilter[] intentFiltersArray;
    private String[][] techListsArray;
    private NfcAdapter mAdapter;
    private PendingIntent pendingIntent;
    private TextView mTransBytes;
    private TextView mHexId;
    private TextView mInfo;
    private EditText mWriteInfo;
    private MifareClassic mNfc;
    private byte[] mUid;

    private static final byte[] KEY_A = new byte[] {
            (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF
    };

//    @SuppressLint("UnspecifiedImmutableFlag")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mAdapter = NfcAdapter.getDefaultAdapter(this);

        pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this,
                getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
        IntentFilter ndef = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
        try {
            ndef.addDataType("*/*");
        } catch (MalformedMimeTypeException e) {
            throw new RuntimeException();
        }

        intentFiltersArray = new IntentFilter[] { ndef, };
        techListsArray = new String[][] { new String[] { MifareClassic.class.getName() } };

        // Initialise TextView fields
        mHexId = (TextView) findViewById(R.id.hexId);
        mTransBytes = (TextView) findViewById(R.id.transceivableBytes);
        mInfo = (TextView) findViewById(R.id.infoView);
        mWriteInfo = (EditText) findViewById(R.id.writeInfo);
    }

    private String bytes2str(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (int b : bytes) {
            if (b < 0) b += 256;
            if (b < 0x10) sb.append('0');
            sb.append(Integer.toHexString(b));
            sb.append(" ");
        }
        return sb.toString();
    }

    private static byte[] str2bytes(String str) {
        byte[] bytes = new byte[16];
        char[] chars = str.toLowerCase().toCharArray();
        for (int i = 0; i < bytes.length; ++i) {
            int ch0 = 2*i >= chars.length
                    ? 0
                    : chars[2*i] >= 'a'
                    ? chars[2*i] - 'a' + 10
                    : chars[2*i] - '0';
            int ch1 = 2*i+1 >= chars.length
                    ? 0
                    : chars[2*i+1] >= 'a'
                    ? chars[2*i+1] - 'a' + 10
                    : chars[2*i+1] - '0';
            bytes[i] = (byte)(ch0 * 16 + ch1);
        }
        return bytes;
    }


    private String readNfc() throws IOException {
        StringBuilder sb = new StringBuilder();
        String res;

        // c6 87 20 30 51 08 04 00 02 5c bc 94 5f be a9 1d
        // 00 00 00 00 ..
        // ..
        // (total 64 blocks)
        for (int blockId = 0; blockId < 64; ++blockId) {
            if (blockId % 4 == 0) {
                int sectorId = blockId / 4;
                sb.append("# ").append(sectorId).append("\n");
                if (!mNfc.authenticateSectorWithKeyA(sectorId, KEY_A)) { // 调用库方法
                    // if (!authenticate(sectorId, KEY_A)) // 手动拼字节 (参考(照抄)了库方法)
                    Log.e("auth failed", "for sector " + sectorId);
                }
            }
            res = bytes2str(mNfc.readBlock(blockId)); // 调用库方法
            // res = getHex(readBlock(blockId)); // 手动拼字节 (参考(照抄)了库方法)
            sb.append(res).append("\n");
            Log.v("read", res);
        }
        return sb.toString();
    }

    private boolean authenticate(int sector, byte[] key) throws IOException {
//        validateSector(sector);
//        checkConnected();

        byte[] cmd = new byte[12];

        // First byte is the command
        cmd[0] = 0x60; // phHal_eMifareAuthentA

        // Second byte is block address
        // Authenticate command takes a block address. Authenticating a block
        // of a sector will authenticate the entire sector.
        cmd[1] = (byte) mNfc.sectorToBlock(sector);

        // Next 4 bytes are last 4 bytes of UID
        System.arraycopy(mUid, mUid.length - 4, cmd, 2, 4);

        // Next 6 bytes are key
        System.arraycopy(key, 0, cmd, 6, 6);

        try {
            if (mNfc.transceive(cmd) != null) {
                return true;
            }
        } catch (TagLostException e) {
            throw e;
        } catch (IOException e) {
            // No need to deal with, will return false anyway
        }
        return false;
    }

    private byte[] readBlock(int blockIndex) throws IOException {
//        validateBlock(blockIndex);
//        checkConnected();

        byte[] cmd = { 0x30, (byte) blockIndex };
        return mNfc.transceive(cmd);
    }

    @Override
    public void onPause() {
        super.onPause();
        mAdapter.disableForegroundDispatch(this);
    }

    @Override
    public void onResume() {
        super.onResume();
        mAdapter.enableForegroundDispatch(this, pendingIntent,
                intentFiltersArray, techListsArray);
    }

    @Override
    public void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        Log.v("tag", "In onNewIntent");

        Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
        mNfc = MifareClassic.get(tag);
        try {
            mNfc.connect();
            Log.v("tag", "connected.");

            mUid = mNfc.getTag().getId();
            Log.v("tag", "Got id from tag:" + bytes2str(mUid));
            mHexId.setText(bytes2str(mUid));
            mTransBytes.setText(String.valueOf(mNfc.getMaxTransceiveLength()));

            String block = readNfc();
            mInfo.setText(block);
        } catch (IOException e) {
            Log.e("IO Exception", "something wrong", e);
        } finally {
            if (mNfc != null) {
                try {
                    mNfc.close();
                } catch (IOException e) {
                    Log.v("tag", "error closing the tag");
                }
            }
        }
    }

    /**
     * 写入 NFC 数据
     * @param view
     */
    public void writeNfc(View view) throws IOException {
        String data = String.valueOf(mWriteInfo.getText());
        Log.v("writeNfc", data);
        try {
            mNfc.connect();
            Log.v("tag", "connected.");

            int blockId = 1;
            int sectorId = blockId / 4;
            if (!mNfc.authenticateSectorWithKeyA(sectorId, KEY_A)) { // 调用库方法
                Log.e("auth failed", "for sector " + sectorId);
            }
            mNfc.writeBlock(blockId, str2bytes(data));
            Toast.makeText(this, "NFC 写入成功", Toast.LENGTH_SHORT).show();
        } catch (IOException e) {
            Log.e("IO Exception", "something wrong", e);
        } finally {
            if (mNfc != null) {
                try {
                    mNfc.close();
                } catch (IOException e) {
                    Log.v("tag", "error closing the tag");
                }
            }
        }
    }
}
